﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GraphicsFramework.LinearSystems
{
    public class Matrix
    {
        #region Variables

        private float[,] matrix;

        #endregion

        #region Constructors

        public Matrix()
        {
            matrix = new float[1, 1];
        }

        public Matrix(int rows, int columns)
        {
            matrix = new float[rows, columns];
        }

        public Matrix(float[,] value)
        {
            int rows = value.GetLength(0);
            int columns = value.GetLength(1);
            matrix = new float[columns, rows];

            Array.Copy(value, matrix, value.Length);
        }

        #endregion

        #region Inner Functions

        public void Transpose()
        {
            matrix = Transpose(matrix);
        }

        public void ScalarAddition(float[,] matrix)
        {
            int rows = this.matrix.GetLength(0);
            int columns = this.matrix.GetLength(1);

            if ((rows == matrix.GetLength(0)) && (columns == matrix.GetLength(1)))
            {
                for (int row = 0; row < rows; row++)
                    for (int col = 0; col < columns; col++)
                    {
                        this.matrix[row, col] += matrix[row, col];
                    }
            }
            else
            {
                throw new InvalidOperationException("Dimensions do not match.");
            }
        }

        public void ScalarSubtraction(float[,] matrix)
        {
            int rows = this.matrix.GetLength(0);
            int columns = this.matrix.GetLength(1);

            if ((rows == matrix.GetLength(0)) && (columns == matrix.GetLength(1)))
            {
                for (int row = 0; row < rows; row++)
                    for (int col = 0; col < columns; col++)
                    {
                        this.matrix[row, col] -= matrix[row, col];
                    }
            }
            else
            {
                throw new InvalidOperationException("Dimensions do not match.");
            }
        }

        public void ScalarMultiplication(float[,] matrix)
        {
            int rows = this.matrix.GetLength(0);
            int columns = this.matrix.GetLength(1);

            if ((rows == matrix.GetLength(0)) && (columns == matrix.GetLength(1)))
            {
                for (int row = 0; row < rows; row++)
                    for (int col = 0; col < columns; col++)
                    {
                        this.matrix[row, col] *= matrix[row, col];
                    }
            }
            else
            {
                throw new InvalidOperationException("Dimensions do not match.");
            }
        }

        public void ScalarDivision(float[,] matrix)
        {
            int rows = this.matrix.GetLength(0);
            int columns = this.matrix.GetLength(1);

            if ((rows == matrix.GetLength(0)) && (columns == matrix.GetLength(1)))
            {
                for (int row = 0; row < rows; row++)
                    for (int col = 0; col < columns; col++)
                    {
                        this.matrix[row, col] /= matrix[row, col];
                    }
            }
            else
            {
                throw new InvalidOperationException("Dimensions do not match.");
            }
        }

        public void MatrixMultiplication(float[,] matrix)
        {
            this.matrix = MatrixMultiplication(this.matrix, matrix);
        }
        
        public void Zero()
        {
            Array.Clear(matrix, 0, matrix.Length);
        }

        #endregion

        #region Static Functions

        public static float[,] Transpose(float[,] matrix)
        {
            int rows = matrix.GetLength(0);
            int columns = matrix.GetLength(1);
            float[,] output = new float[columns, rows];

            for (int row = 0; row < rows; row++)
                for (int col = 0; col < columns; col++)
                {
                    output[col, row] = matrix[row, col];
                }

            return output;
        }

        public static float[,] ScalarAddition(float[,] matrix1, float[,] matrix2)
        {
            int rows = matrix1.GetLength(0);
            int columns = matrix1.GetLength(1);
            float[,] output = new float[rows, columns];

            if ((rows == matrix2.GetLength(0)) && (columns == matrix2.GetLength(1)))
            {
                for (int row = 0; row < rows; row++)
                    for (int col = 0; col < columns; col++)
                    {
                        output[row, col] = matrix1[row, col] + matrix2[row, col];
                    }
            }
            else
            {
                throw new InvalidOperationException("Dimensions do not match.");
            }

            return output;
        }

        public static float[,] ScalarSubtraction(float[,] matrix1, float[,] matrix2)
        {
            int rows = matrix1.GetLength(0);
            int columns = matrix1.GetLength(1);
            float[,] output = new float[rows, columns];

            if ((rows == matrix2.GetLength(0)) && (columns == matrix2.GetLength(1)))
            {
                for (int row = 0; row < rows; row++)
                    for (int col = 0; col < columns; col++)
                    {
                        output[row, col] = matrix1[row, col] - matrix2[row, col];
                    }
            }
            else
            {
                throw new InvalidOperationException("Dimensions do not match.");
            }

            return output;
        }

        public static float[,] ScalarMultiplication(float[,] matrix1, float[,] matrix2)
        {
            int rows = matrix1.GetLength(0);
            int columns = matrix1.GetLength(1);
            float[,] output = new float[rows, columns];

            if ((rows == matrix2.GetLength(0)) && (columns == matrix2.GetLength(1)))
            {
                for (int row = 0; row < rows; row++)
                    for (int col = 0; col < columns; col++)
                    {
                        output[row, col] = matrix1[row, col] * matrix2[row, col];
                    }
            }
            else
            {
                throw new InvalidOperationException("Dimensions do not match.");
            }

            return output;
        }

        public static float[,] ScalarDivision(float[,] matrix1, float[,] matrix2)
        {
            int rows = matrix1.GetLength(0);
            int columns = matrix1.GetLength(1);
            float[,] output = new float[rows, columns];

            if ((rows == matrix2.GetLength(0)) && (columns == matrix2.GetLength(1)))
            {
                for (int row = 0; row < rows; row++)
                    for (int col = 0; col < columns; col++)
                    {
                        output[row, col] = matrix1[row, col] / matrix2[row, col];
                    }
            }
            else
            {
                throw new InvalidOperationException("Dimensions do not match.");
            }

            return output;
        }

        public static float[,] MatrixMultiplication(float[,] matrix1, float[,] matrix2)
        {
            int rows = matrix1.GetLength(0);
            int column1 = matrix1.GetLength(1);
            int column2 = matrix2.GetLength(1);

            float[,] output = new float[matrix1.GetLength(0), matrix2.GetLength(1)];
            float result;

            if (matrix1.GetLength(1) == matrix2.GetLength(0))
            {
                for (int row1 = 0; row1 < rows; row1++)
                {
                    for (int col1 = 0; col1 < column2; col1++)
                    {
                        result = 0;

                        for (int col2 = 0; col2 < column1; col2++)
                        {
                            result += matrix1[row1, col2] * matrix2[col2, col1];
                        }

                        output[row1, col1] = result;
                    }
                }
            }
            else
            {
                throw new InvalidOperationException("Dimensions are invalid for matrix multplication.");
            }

            return output;
        }

        #endregion
    }
}
